package com.hotel.front;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.Util;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.labs.repackaged.org.json.JSONException;
import com.google.gson.Gson;
import com.hotel.BL.ChargeLogic;
import com.hotel.BL.PackageLogic;
import com.hotel.BL.ReservationLogic;
import com.hotel.BL.RoomTypeLogic;
import com.hotel.BL.TaxLogic;

import data.Charge;
import data.PackageByDate;
import data.PackagePrice;
import data.Reservation;
import data.RoomType;
import data.SubReservation;
import data.passingData.ResultData;

@SuppressWarnings("serial")
public class PackageService extends HttpServlet{
	SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd",Locale.ENGLISH);
	//http://localhost:8080/packageService?doClass=addPackage&Name=e&shortName=t&lengthOfStay=1&Description=w&packagepriceListString=2
	//http://localhost:8080/packageService?doClass=editPackage&packageId=885&Name=eeeee&shortName=tttttt&lengthOfStay=1&Description=w&packagepriceListString=2
	//http://localhost:8080/packageService?doClass=delPackage&packageId=885
	//http://localhost:8080/packageService?doClass=changeStatus&packageId=885
	//http://localhost:8080/packageService?doClass=getPackageByDate&packageId=885
	//http://localhost:8080/packageService?doClass=editPackageByDate&packageId=&pacdateId=&dateF=&dateTo=
	
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {
		try{
			String doClass = req.getParameter("doClass");
			if(doClass.equals("addPackage")){
				AddPackage(req,resp);
			}else if(doClass.equals("editPackage")){
				EditPackage(req,resp);
			}else if(doClass.equals("delPackage")){
				DeletePackage(req,resp);
			}else if(doClass.equals("changeStatus")){
				ChangeStatus(req,resp);
			}else if(doClass.equals("getPackage")){
				GetPackagePrice(req,resp);
			}else if(doClass.equals("getAdultExBed")){
				GetAdultExBed(req,resp);
			}else if(doClass.equals("getChildExBed")){
				GetChildExBed(req,resp);
			}else if(doClass.equals("getExBedPrice")){
				GetExBedPrice(req,resp);
			}else if(doClass.equals("getPackageByDate")){
				GetPackageByDate(req,resp);
			}else if(doClass.equals("editPackageByDate")){
				EditPackageByDate(req,resp);
			}else if(doClass.equals("EditPackageCustomPrice"))
				EditPackageCustomPrice(req,resp);
			else if(doClass.equals("editPackageByDateFromGroup"))
				editPackageByDateFromGroup(req,resp);
			else if(doClass.equals("editPricePackByDateFromGroup"))
				editPricePackByDateFromGroup(req,resp);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void AddPackage(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException
	{	
		HttpSession session=req.getSession(true);	
		long hotelId = (Long) session.getAttribute("hotelId");
		String Name = req.getParameter("Name");
		String shortName = req.getParameter("shortName");
		long lengthOfStay = Long.parseLong(req.getParameter("lengthOfStay"));
		String Description = req.getParameter("Description");
		String packagepriceListString = req.getParameter("packagepriceListString");

		Object obj = new PackageLogic(req,resp).AddPackage(hotelId,Name,shortName,lengthOfStay,Description,packagepriceListString);
		Gson gson = new Gson();

		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(obj));
	}

	private void EditPackage(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException
	{	
		long packageId = Long.parseLong(req.getParameter("packageId"));
		String Name = req.getParameter("Name");
		String shortName = req.getParameter("shortName");
		long lengthOfStay = Long.parseLong(req.getParameter("lengthOfStay"));
		String Description = req.getParameter("Description");
		String packagepriceListString = req.getParameter("packagepriceListString");
		
		Object obj = new PackageLogic(req,resp).EditPackage(packageId,Name,shortName,lengthOfStay,Description,packagepriceListString);
		Gson gson = new Gson();

		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(obj));

	}

	private void DeletePackage(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException
	{	
		long packageId = Long.parseLong(req.getParameter("packageId"));
		
		Object obj = new PackageLogic(req,resp).DeletePackage(packageId);
		Gson gson = new Gson();

		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(obj));
	}
	
	private void ChangeStatus(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException
	{	
		long packageId = Long.parseLong(req.getParameter("packageId"));

		Object obj = new PackageLogic(req,resp).ChangeStatus(packageId);
		Gson gson = new Gson();

		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(obj));
	}
	
	private void GetPackagePrice(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException
	{	
		long packageId = Long.parseLong(req.getParameter("packageId"));
		long roomtype = Long.parseLong(req.getParameter("roomtype"));
		data.Package obj = new PackageLogic(req,resp).GetPackage(packageId);
		double price = 0;
		List<PackagePrice> a = obj.getPackagepriceList();
		for(int i=0;i<a.size();i++){
			if(roomtype == a.get(i).getRoomtype().getId()){
				price = a.get(i).getPriceperNight();
			}
		}
		Gson gson = new Gson();

		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(price));
	}
	
	private void GetAdultExBed(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException
	{	
		long packageId = Long.parseLong(req.getParameter("packageId"));
		long roomtype = Long.parseLong(req.getParameter("roomtype"));
		long occA = Long.parseLong(req.getParameter("occA"));
		long occC = Long.parseLong(req.getParameter("occC"));
		RoomType occupancy = new RoomTypeLogic(req,resp).GetRoomType(roomtype);
		long standardOccupancy = occupancy.getStandardOccupancy();
		
		double exBedPrice = 0;
		data.Package obj = new PackageLogic(req,resp).GetPackage(packageId);
		double price = 0;
		List<PackagePrice> a = obj.getPackagepriceList();
		for(int i=0;i<a.size();i++){
			if(roomtype == a.get(i).getRoomtype().getId()){
				price = a.get(i).getPriceperNight();
				exBedPrice = a.get(i).getExtrabed();
			}
		}
		if(standardOccupancy-(occA+occC) < 0){
			price = price+((occA+occC)-standardOccupancy)*exBedPrice;
		}
		Gson gson = new Gson();

		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(price));
	}	
	
	private void GetChildExBed(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException
	{	
		long packageId = Long.parseLong(req.getParameter("packageId"));
		long roomtype = Long.parseLong(req.getParameter("roomtype"));
		long occA = Long.parseLong(req.getParameter("occA"));
		long occC = Long.parseLong(req.getParameter("occC"));
		RoomType occupancy = new RoomTypeLogic(req,resp).GetRoomType(roomtype);
		long standardOccupancy = occupancy.getStandardOccupancy();
		
		double exBedPrice = 0;
		data.Package obj = new PackageLogic(req,resp).GetPackage(packageId);
		double price = 0;
		List<PackagePrice> a = obj.getPackagepriceList();
		for(int i=0;i<a.size();i++){
			if(roomtype == a.get(i).getRoomtype().getId()){
				price = a.get(i).getPriceperNight();
				exBedPrice = a.get(i).getExtrabed();
			}
		}
		if(standardOccupancy-(occA+occC) < 0){
			price = price+((occA+occC)-standardOccupancy)*exBedPrice;
		}
		Gson gson = new Gson();

		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(price));
	}	
	
	private void GetExBedPrice(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException
	{	
		long packagebydateId = Long.parseLong(req.getParameter("packagebydate"));
		long roomtype = Long.parseLong(req.getParameter("roomtype"));
		long exAmount = Long.parseLong(req.getParameter("extrabed"));
		long resId = Long.parseLong(req.getParameter("resId"));
		Reservation res = new ReservationLogic(req,resp).GetReservation(resId);
		double exBedPrice = 0;
		PackageByDate pbd = new PackageLogic(req,resp).GetPackageByDate(packagebydateId);
		data.Package obj = pbd.getPackages();
		double price = 0;
		double upchargeperson = 0;
		List<PackagePrice> a = obj.getPackagepriceList();
		for(int i=0;i<a.size();i++){
			if(roomtype == a.get(i).getRoomtype().getId()){
				price = a.get(i).getPriceperNight();
				exBedPrice = a.get(i).getExtrabed();
				upchargeperson = a.get(i).getUpchargePerson();
				break;
			}
		}
		long guestAmount = res.getAdult()+res.getChild();
		long standdard = res.getRoomType().getStandardOccupancy();
		long other = 0;
		if((guestAmount - standdard)-exAmount > 0){
			other = (guestAmount - standdard)-exAmount;
		}
		price = res.getSpan() * ( price+(exAmount*exBedPrice)+((other*upchargeperson))) ;
		Gson gson = new Gson();

		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(price));
	}

	private void GetPackageByDate(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException
	{	
		String packageId = req.getParameter("packageId");
		Gson gson = new Gson();
		List<PackageByDate> list = new ArrayList<PackageByDate>();
		PackageLogic packageLogic = new PackageLogic(req,resp);
		if(packageId != null){
			String [] pacIdList = packageId.trim().split(",");
			for(int i=0;i<pacIdList.length;i++){
				PackageByDate p = packageLogic.GetPackageByDate(Long.parseLong(pacIdList[i]));
				
				list.add(p);
			}
		}
		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(list));
	}
	
	private void EditPackageByDate(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException, ParseException
	{	
		String resId = req.getParameter("resId");
		String packageId = req.getParameter("packageId");
		long pacdateId = Long.parseLong(req.getParameter("pacByDateId"));
		Date dateF =formatter.parse(req.getParameter("dateForm"));
		Date dateT =formatter.parse(req.getParameter("dateTo"));
		String NewTodate = req.getParameter("NewTodate");
		Gson gson = new Gson();
		String sessionCheck = Util.checkSessionEntity(req,resp);
		if(sessionCheck==null||sessionCheck.equals("")){
			resp.setContentType("text/plain; charset=UTF-8");
			resp.getWriter().println(gson.toJson(new ResultData(false)));
			return;
		}
		PackageByDate addP = new PackageByDate();
		PackageLogic packageLogic = new PackageLogic(req,resp);
		ReservationLogic resvLogic = new ReservationLogic(req,resp);
		Reservation r = null;
		if(NewTodate == null){
			packageLogic.EditPackageByDate(pacdateId, dateF, dateT, packageId);
			r = resvLogic.GetReservation(Long.parseLong(resId));
		}else
		{
			Date newDateTo =formatter.parse(NewTodate);
			if(newDateTo.before(dateT)){
				addP = packageLogic.AddPackageByDate(packageId,newDateTo, dateT );
				packageLogic.EditPackageByDate(pacdateId, dateF, newDateTo, packageId);
				packageLogic.AddtoReservation(Long.parseLong(resId),String.valueOf(addP.getId()));
			}else if(newDateTo.after(dateT)){
				packageLogic.EditPackageByDate(pacdateId, dateF, newDateTo, packageId);
				r = resvLogic.GetReservation(Long.parseLong(resId));
				List<PackageByDate> listPByDate = r.getPackageByDate();
				for(int i=0;i<listPByDate.size();i++){
					PackageByDate pByDate = listPByDate.get(i);
					if(pByDate.getDateFrom().after(dateF) && !pByDate.getDateTo().after(newDateTo))
					{
						packageLogic.DeletePackageByDate(pByDate.getId());
						listPByDate.remove(pByDate);
						i--;
					}
					else if(pByDate.getDateFrom().before(newDateTo) && pByDate.getDateTo().after(newDateTo))
					{
						packageLogic.EditPackageByDate(pByDate.getId(), newDateTo, pByDate.getDateTo(), packageId);
					}
				}
				r.setPackageByDate(listPByDate);
				resvLogic.UpdateReservation(r);
			}
		}
		
		r = resvLogic.GetReservation(Long.parseLong(resId));
		
		List<SubReservation> subList = r.getSubReservationList();
		if(subList == null || subList.size() == 0)
		{
			SubReservation sub = new SubReservation();
			sub.CloneReservation(r);
			subList.add(sub);
		}
		new ChargeLogic(req,resp).CalCharge(r.getHotelId(), subList, 
				r.getPackageByDate(),r.getDate(),r.getDateTo());
		
		HashMap<String,Object> returnValue = new HashMap<String,Object>();
		returnValue.put("packageByDate", r.getPackageByDate());
		returnValue.put("charge", r.getChargeList());
		
		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(returnValue));
	}
	
	private void EditPackageCustomPrice(HttpServletRequest req, HttpServletResponse resp) throws EntityNotFoundException, IOException
	{
		long resId = Long.parseLong(req.getParameter("resId"));
		long packageByDateId = Long.parseLong(req.getParameter("packageByDateId"));
		double price = Double.parseDouble(req.getParameter("price"));
		Gson gson = new Gson();
		String sessionCheck = Util.checkSessionEntity(req,resp);
		if(sessionCheck==null||sessionCheck.equals("")){
			resp.setContentType("text/plain; charset=UTF-8");
			resp.getWriter().println(gson.toJson(new ResultData(false)));
			return;
		}
		TaxLogic taxLogic = new TaxLogic(req,resp);
		ChargeLogic chargeLogic = new ChargeLogic(req,resp);
		PackageLogic packageLogic = new PackageLogic(req,resp);
		PackageByDate packageByDate = packageLogic.GetPackageByDate(packageByDateId);
		
		ReservationLogic resLogic = new ReservationLogic(req,resp);
		Reservation res = resLogic.GetReservation(resId);
		
		List<Charge> roomChargeList = res.getChargeList();
		Map<Integer,Charge> editChargeList = new HashMap<Integer,Charge>();
		
		double minPrice = 0;
		double sumOfNonFix = 0;
		
		Collections.sort(roomChargeList, new Comparator<Charge>(){
            public int compare(Charge c1,Charge c2){
                return (c1.getChargeDate().before(c2.getChargeDate()))? -1: 1;
          }});
		
		Date firstChargeOfResv = roomChargeList.get(0).getChargeDate();
		double TaxPerResv = taxLogic.GetTaxPerReservation(res.getHotelId());
		for(int i=0;i<roomChargeList.size();i++)
		{
			Charge roomCharge = roomChargeList.get(i);
			if(!roomCharge.getChargeDate().before(packageByDate.getDateFrom()) && roomCharge.getChargeDate().before(packageByDate.getDateTo()))
			{
				editChargeList.put(i, roomCharge);
				//TODO: remove check by date
				Date cDate = Util.GetOnlyDate(new Date());
				if(roomCharge.getChargeDate().before(cDate))
				//if(roomCharge.isLock())
					minPrice += roomCharge.getChargeAmount();
				else
					sumOfNonFix += roomCharge.getChargeAmount();
			}
		}
		
		if(firstChargeOfResv.before(packageByDate.getDateFrom()))
			TaxPerResv = 0;
		
		HashMap<String,Object> returnValue = new HashMap<String,Object>();
		if(price < minPrice)//cannot be edit price lower than minimum
		{
			returnValue.put("result", false);
			returnValue.put("charge", null);
		}
		else{
			if(minPrice == 0){
				minPrice = TaxPerResv;
				sumOfNonFix -= TaxPerResv;
			}
			resLogic.UpdateReservation(res);
			
			Set<Integer> keys = editChargeList.keySet();
			for(int key : keys)
			{
				Charge roomCharge = editChargeList.get(key);
				//TODO: remove check by date
				Date cDate = Util.GetOnlyDate(new Date());
				if(!roomCharge.getChargeDate().before(cDate))
				//if(!roomCharge.isLock())
				{
					double currentPrice = roomCharge.getChargeAmount();
					double newPrice = ((price-minPrice)*currentPrice)/(sumOfNonFix);

					if(roomCharge.getChargeDate().equals(firstChargeOfResv))
						newPrice = (((price-minPrice)*(currentPrice-TaxPerResv))/sumOfNonFix)+TaxPerResv;
					
					HashMap<String,Double> calTax = taxLogic.CalRoomTax(res.getHotelId(),newPrice,key,true);
					chargeLogic.EditChargeAmountWithDiscount(roomCharge.getId(), calTax.get("costIncludeTax"),calTax.get("tax"));
				}
			}
			returnValue.put("result", true);
			returnValue.put("charge", res.reloadChargeList());
		}
		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(returnValue));
	}
	
	private void editPackageByDateFromGroup(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException, ParseException
	{
		Gson gson = new Gson();
		String sessionCheck = Util.checkSessionEntity(req,resp);
		if(sessionCheck==null||sessionCheck.equals("")){
			resp.setContentType("text/plain; charset=UTF-8");
			resp.getWriter().println(gson.toJson(new ResultData(false)));
			return;
		}
		String resId = req.getParameter("resId");
		String packageId = req.getParameter("packageId");
		String[] pacdateId = req.getParameter("pacByDateId").split(",");
		PackageLogic packageLogic = new PackageLogic(req,resp);
		ReservationLogic resvLogic = new ReservationLogic(req,resp);
		for(String packId : pacdateId)
			packageLogic.EditPackageByDate(Long.parseLong(packId), packageId);
		
		Reservation r = resvLogic.GetReservation(Long.parseLong(resId));
		
		List<SubReservation> subList = r.getSubReservationList();
		if(subList == null || subList.size() == 0)
		{
			SubReservation sub = new SubReservation();
			sub.CloneReservation(r);
			subList.add(sub);
		}
		List<Charge> list = new ChargeLogic(req,resp).CalCharge(r.getHotelId(), subList, 
				r.getPackageByDate(),r.getDate(),r.getDateTo());
		
		List<HashMap<String,Object>> returnValue = new ArrayList<HashMap<String,Object>>();
		for(int i=0;i<list.size();i++)
		{
			HashMap<String,Object> rs = new HashMap<String,Object>();
			rs.put("id", list.get(i).getId());
			rs.put("amount", list.get(i).getChargeAmount());
			rs.put("discount", list.get(i).getDiscount());
			rs.put("tax", list.get(i).getTax());
			returnValue.add(rs);
		}
		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(returnValue));
	}
	
	private void editPricePackByDateFromGroup(HttpServletRequest req, HttpServletResponse resp) throws IOException, JSONException, ServletException, EntityNotFoundException, ParseException
	{
		Gson gson = new Gson();
		HttpSession session = req.getSession(true);
		long hotelId = 1;
		if (session.getAttribute("hotelId") != null)
			hotelId = (Long) session.getAttribute("hotelId");
		String sessionCheck = Util.checkSessionEntity(req,resp);
		if(sessionCheck==null||sessionCheck.equals("")){
			resp.setContentType("text/plain; charset=UTF-8");
			resp.getWriter().println(gson.toJson(new ResultData(false)));
			return;
		}
		String resId = req.getParameter("resId");
		double price = Double.parseDouble(req.getParameter("price"));
		ReservationLogic resvLogic = new ReservationLogic(req,resp);
		ChargeLogic chargeLogic = new ChargeLogic(req,resp);
		TaxLogic taxLogic = new TaxLogic(req,resp);
		
		Reservation res = resvLogic.GetReservation(Long.parseLong(resId));
		List<Charge> charges = res.getChargeList();
		double total = 0;
		double cannotchange = 0;
		for(Charge charge:charges){
			//TODO: remove check by date
			Date cDate = Util.GetOnlyDate(new Date());
			if(!charge.getChargeDate().before(cDate))
			//if(!roomCharge.isLock())
				total+=charge.getChargeAmount();
			else
				cannotchange+=charge.getChargeAmount();
		}
		List<HashMap<String,Object>> rs = new ArrayList<HashMap<String,Object>>();
		if(cannotchange<price)
			for(Charge charge:charges){
				//TODO: remove check by date
				Date cDate = Util.GetOnlyDate(new Date());
				if(charge.getChargeDate().before(cDate))
				//if(roomCharge.isLock())
					continue;
				double amountWithoutDiscount = charge.getChargeAmount() + charge.getDiscount();
				double taxPerRes = taxLogic.GetTaxPerReservation(hotelId);
				double fixCost = (charge.getChargeDate().equals(res.getDate()))? taxPerRes:0;
				double chargeAmount = (((charge.getChargeAmount()-fixCost) * (price-cannotchange-taxPerRes))/(total-taxPerRes))+fixCost;
				int x = (charge.getChargeDate().equals(res.getDate()))? 0:1;
				HashMap<String,Double> calTax = taxLogic.CalRoomTax(hotelId,chargeAmount,x,true);
				chargeLogic.EditChargeAmount(charge.getId(), chargeAmount, amountWithoutDiscount - chargeAmount,calTax.get("tax"));
				HashMap<String,Object> hash = new HashMap<String,Object>();
				hash.put("amount",chargeAmount);
				hash.put("discount",amountWithoutDiscount - chargeAmount);
				hash.put("tax",calTax.get("tax"));
				hash.put("id", charge.getId());
				rs.add(hash);
			}
		resp.setContentType("text/plain; charset=UTF-8");
		resp.getWriter().println(gson.toJson(rs));
	}
}
